package com.yungam.imcs.controller.logistics;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.yungam.common.bean.process.ProductProcess;
import com.yungam.common.bean.process.ProductionProcess;
import com.yungam.common.bean.product.ProductInfo;
import com.yungam.common.response.RetDTO;
import com.yungam.common.service.ProductInfoService;
import com.yungam.common.service.ProductProcessService;
import com.yungam.common.service.ProductionProcessService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Api(tags = "物流优化页面接口")
@RestController
@RequestMapping("/logicBetter")
public class LogisticsOptimizationController {

    @Resource
    private ProductInfoService productInfoService;
    @Resource
    private ProductProcessService productProcessService;
    @Resource
    private ProductionProcessService productionProcessService;

    @ApiOperation("获取所有的产品数据")
    @GetMapping("/products")
    public RetDTO<?> getAllProducts() {
        // 所有的产品信息
        List<ProductInfo> products = productInfoService.list();
        return RetDTO.data(products);
    }

    /* 产品对应的工序物流周期总计 & 各工序物流耗时统计 */
    @ApiOperation("获取页面数据")
    @GetMapping("/data")
    public RetDTO<?> getData(@ApiParam("产品号") String productNo) {
        Map<String, Object> ans = Maps.newHashMap(); // 返回值

        // ①根据产品号查询该产品的所有工序 & ②将前后工序保存在 Map 中 & ③将工序的开始时间保存在 map 中
        // 物流耗时 = 后工序.startTime - 前工序.endTime
        Map<String, String> val = Maps.newLinkedHashMap(), linkedProcess = Maps.newHashMap();
        Map<String, Date> dateMap = Maps.newHashMap();

        // ①
        List<ProductProcess> processes = productProcessService.list(new QueryWrapper<ProductProcess>()
                .eq("product_no", productNo).orderByAsc("process_id"));
        List<String> processNos = processes.stream().map(ProductProcess::getProcessNo).collect(Collectors.toList());
        for (ProductProcess p : processes) val.put(p.getProcessNo(), p.getProcessName());
        ans.put("process", val); // 返回值

        // ②
        int length = processNos.size();
        for (int i = 0; i < length; i++) {
            if (i == length - 1) {
                linkedProcess.put(processNos.get(i), null); // 最后一道工序无耗时
                break;
            }
            String cur = processNos.get(i), after = processNos.get(i + 1);
            linkedProcess.put(cur, after);
        }

        // ③
        // 查询该产品的所有工序质量信息
        List<ProductionProcess> list = productionProcessService.list(new QueryWrapper<ProductionProcess>()
                .eq("product_no", productNo));
        for (ProductionProcess pp : list) dateMap.put(pp.getProductSn() + "-" + pp.getProcessNo(), pp.getStartTime());

        // 没有工序质量信息则直接返回
        if (list.size() == 0) {
            ans.put("cardValues", Lists.newArrayList(0, 0, 0, 0));
            ans.put("chartData", Maps.newHashMap());
            return RetDTO.data(ans);
        }

        // ④ 按照罐号分组计算周期耗时 & ⑤ 按照工序号分组计算工序耗时
        Map<String, List<Long>> processMap = Maps.newHashMap(), productSnMap = Maps.newHashMap();

        // ④
        for (ProductionProcess pp : list) {
            String processNo = pp.getProcessNo(), productSn = pp.getProductSn(); // 工序号
            // 获取下一道工序的开始时间
            if (!linkedProcess.containsKey(processNo) || linkedProcess.get(processNo) == null) continue;
            String key = productSn + "-" + linkedProcess.get(processNo);
            Date nextStart = dateMap.get(key);
            Long diff = calculate(pp.getEndTime(), nextStart); // 耗时
            // 工序耗时
            List<Long> item = processMap.getOrDefault(processNo, Lists.newArrayList());
            item.add(diff);
            processMap.put(processNo, item);
            // 周期耗时
            List<Long> item2 = productSnMap.getOrDefault(productSn, Lists.newArrayList());
            item2.add(diff);
            productSnMap.put(productSn, item2);
        }

        // 整理数据
        long cycleMax = Long.MIN_VALUE, cycleMin = Long.MAX_VALUE, cycleSum = 0L;
        for (String s : productSnMap.keySet()) {
            List<Long> item = productSnMap.get(s);
            Long time = 0L;
            for (Long a : item)  time += a;
            cycleMax = Math.max(cycleMax, time);
            cycleMin = Math.min(cycleMin, time);
            cycleSum += time;
        }
        int len = productSnMap.keySet().size();
        Long cycleAvg = len > 0 ? cycleSum / len : 0;
        Double cMax = formatHour(cycleMax), cMin = formatHour(cycleMin), cAvg = formatHour(cycleAvg);
        double percent = cMin < cAvg ? (cAvg - cMin) / cAvg : 0;
        if (percent != 0) percent = Double.parseDouble(String.format("%.2f", percent * 100)); // 取两位小数

        ans.put("cardValues", Lists.newArrayList(cMax, cMin, cAvg, percent)); // 返回值

        // ⑤
        Map<String, Object> chartData = Maps.newHashMap();
        for (String s : processMap.keySet()) {
            List<Long> item = processMap.get(s);
            long processMin = Long.MAX_VALUE, processMax = Long.MIN_VALUE, processSum = 0, processAvg;
            for (Long a : item) {
                processMin = Math.min(processMin, a);
                processMax = Math.max(processMax, a);
                processSum += a;
            }
            int size = item.size();
            processAvg = size > 0 ? processSum / size : 0;
            Double pMin = formatHour(processMin), pMax = formatHour(processMax), pAvg = formatHour(processAvg);
            double pPercent = pMin < pAvg ? (pAvg - pMin) / pAvg : 0;
            if (pPercent != 0) pPercent = Double.parseDouble(String.format("%.1f", pPercent * 100)); // 取两位小数
            Map<String, Object> cur = Maps.newHashMap();
            cur.put("min", pMin);
            cur.put("max", pMax);
            cur.put("avg", pAvg);
            cur.put("percent", pPercent);
            chartData.put(s, cur);
        }

        ans.put("chartData", chartData); // 返回值

        return RetDTO.data(ans);
    }

    /* 计算耗时 */
    private Long calculate(Date start, Date end) {
        if (start == null || end == null) return 0L;
        long diff = end.getTime() - start.getTime();
        if (diff < 0) diff = 0;
        return diff;
    }

    /* 毫秒 => 小时 */
    private Double formatHour(Long l) {
        double d = Double.parseDouble(String.valueOf(l));
        return Double.parseDouble(String.format("%.2f", d / (1000 * 60 * 60)));
    }

}
